#include <all_header.h>

#undef FLASH_BANK_SIZE  /// 有时读取时会报错
#define FLASH_BANK_SIZE				(1024*1024)

static QueueHandle_t que;
static EventGroupHandle_t event;
static SemaphoreHandle_t mutex;
static ring_buffer_handle_t handle;
static uint8_t buffer[FLASH_STREAM_TEMP_READ_BUFFER_SIZE];
static bool finished;

static int get_bank(uint32_t start) {
	if (start >= FLASH_BANK_SIZE)
		return FLASH_BANK_2;
	return FLASH_BANK_1;
}

static int get_sector(uint32_t start) {
	if (start >= FLASH_BANK_SIZE) {
		return (start - FLASH_BANK_SIZE) / FLASH_SECTOR_SIZE;
	}
	return start / FLASH_SECTOR_SIZE;
}

static void erase_data(uint32_t start_addr, uint32_t size) {
	uint32_t SectorError;
	FLASH_EraseInitTypeDef EraseInitStruct;
	MEM_ZERO(EraseInitStruct);

	EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;
	EraseInitStruct.Banks = get_bank(start_addr);
	EraseInitStruct.Sector = get_sector(start_addr);
	EraseInitStruct.NbSectors = size / FLASH_SECTOR_SIZE;

	if (HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError) != HAL_OK) {
		ASSERT(0);
	}
}

static void program_flash(uint32_t addr, uint8_t *data, uint32_t len) {

	ASSERT(((uint32_t )data) % 4 == 0);

	while (len >= 16) {
		ASSERT(
				HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, FLASH_MAP_ADDR + addr , (uint32_t)data) == HAL_OK);

		len -= 16;
		data += 16;
		addr += 16;
	}

	/// 填充0
	if (len > 0) {
		uint8_t buf[16];
		ASSERT(((uint32_t )buf) % 4 == 0);
		memset(buf, 0, sizeof(buf));
		memcpy(buf, data, len);
		ASSERT(
				HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, FLASH_MAP_ADDR + addr , (uint32_t)data) == HAL_OK);
	}
}

static void channel0_transfer_complete(DMA_HandleTypeDef *dma) {
	ring_buffer_write_cmplt_callback(handle, dma);
}

static void channel1_transfer_complete(DMA_HandleTypeDef *dma) {
	ring_buffer_read_cmplt_callback(handle, dma);
}

static void transfer_failed(DMA_HandleTypeDef *dma) {
	ASSERT(0);
}

void flash_stream_task_init(SemaphoreHandle_t mtx) {
	que = xQueueCreate(10, sizeof(flash_stream_task_state_t));
	ASSERT(que);

	event = xEventGroupCreate();
	ASSERT(event);

	mutex = mtx;

	handle = ring_buffer_create(FLASH_STREAM_BUFFER_SIZE,
			&handle_GPDMA2_Channel0, &handle_GPDMA2_Channel1);
	ASSERT(handle);
	HAL_DMA_RegisterCallback(&handle_GPDMA2_Channel0, HAL_DMA_XFER_CPLT_CB_ID,
			channel0_transfer_complete);
	HAL_DMA_RegisterCallback(&handle_GPDMA2_Channel1, HAL_DMA_XFER_CPLT_CB_ID,
			channel1_transfer_complete);
	HAL_DMA_RegisterCallback(&handle_GPDMA2_Channel0, HAL_DMA_XFER_ERROR_CB_ID,
			transfer_failed);
	HAL_DMA_RegisterCallback(&handle_GPDMA2_Channel1, HAL_DMA_XFER_ERROR_CB_ID,
			transfer_failed);
}

void flash_stream_task_run(void *arg) {
	flash_stream_task_state_t state;
	uint32_t write_start;
	uint32_t write_len;
	while (true) {
		ASSERT(xQueueReceive(que,&state,portMAX_DELAY) == pdTRUE);

		int size;
		bool need_write = false;
		ASSERT(xSemaphoreTake(mutex, portMAX_DELAY) == pdTRUE); // global lock

		switch (state) {
		case Flash_Stream_Task_State_Open:
			finished = false;
			HAL_ICACHE_Disable();
			ASSERT(HAL_FLASH_Unlock() == HAL_OK);
			write_start = 0;
			write_len = 0;
			break;
		case Flash_Stream_Task_State_Close:
			while (ring_buffer_get_readable_size(handle) > 0) {
				size = ring_buffer_read(handle, buffer, sizeof(buffer), 0);
				program_flash(write_start + write_len, buffer, size);
				write_len += size;
			}

			ASSERT(HAL_FLASH_Lock() == HAL_OK);
			HAL_ICACHE_Enable();
			finished = true;
			break;
		case Flash_Stream_Task_State_Erase_Download:
			erase_data(DOWNLOAD_FLASH_START, DOWNLOAD_FLASH_SIZE);
			break;
		case Flash_Stream_Task_State_Erase_BootLoader:
			erase_data(BOOTLOADER_FLASH_START, BOOTLOADER_FLASH_SIZE);
			break;
		case Flash_Stream_Task_State_Erase_App:
			erase_data(APP_FLASH_START, APP_FLASH_SIZE);
			break;
		case Flash_Stream_Task_State_Write_Download:
			write_start = DOWNLOAD_FLASH_START;
			need_write = true;
			break;
		case Flash_Stream_Task_State_Write_BootLoader:
			write_start = BOOTLOADER_FLASH_START;
			need_write = true;
			break;
		case Flash_Stream_Task_State_Write_App:
			write_start = APP_FLASH_START;
			need_write = true;
			break;
		}

		// need write
		if (need_write) {
			while (ring_buffer_get_readable_size(handle) >= sizeof(buffer)) {
				size = ring_buffer_read(handle, buffer, sizeof(buffer), 0);
				ASSERT(size == sizeof(buffer));
				program_flash(write_start + write_len, buffer, size);
				write_len += size;
			}
		}

		xEventGroupSetBits(event, 1);
		xEventGroupClearBits(event, 1);

		ASSERT(xSemaphoreGive(mutex) == pdTRUE); // global unlock
	}
}

void flash_stream_task_set_state(flash_stream_task_state_t state) {
	ASSERT(xQueueSend(que,&state,DEFAULT_WAIT_TICKS) == pdTRUE);
}

int flash_stream_task_write_buffer(uint8_t *data, uint32_t len,
		flash_stream_task_state_t state) {
	if (ring_buffer_get_available_size(handle) >= len) {
		ASSERT(ring_buffer_write(handle, data, len) == len);
		flash_stream_task_set_state(state);
		return len;
	}
	return 0;
}

uint32_t flash_stream_task_get_avaliabe_size(void) {
	return ring_buffer_get_available_size(handle);
}

void flash_stream_task_wait(uint32_t ticks) {
	xEventGroupWaitBits(event, 1, pdTRUE, pdTRUE, ticks);
}

void flash_stream_task_wait_until_finish(void) {
	while (!finished) {
		xEventGroupWaitBits(event, 1, pdTRUE, pdTRUE, portMAX_DELAY);
	}
}

uint32_t flash_stream_task_get_start_addr(void) {
	return DOWNLOAD_FLASH_START + FLASH_MAP_ADDR;
}
